#include "Playero.h"

#include <Utils.h>
#include <ConfigurationFile.h>
#include <FifoEscritura.h>

#include <sstream>

using namespace std;

Playero::Playero()
: m_ReceptorFifoAbierto(false), m_CantPlayerosAvailableIncremented(false)
{
	m_Log = Logger::getInstance();
	
	std::string keySurtidores = ConfigurationFile::getProperty(Properties::KEY_SURTIDORES);
	int cantSurtidores = Utils::convertSrtToInt(ConfigurationFile::getProperty(Properties::CANT_SURTIDORES));
	std::string pathContadorPlayeros = ConfigurationFile::getProperty(Properties::KEY_PLAYEROS_DISP);
	try
	{
		// Creo los surtidores
		m_SurtidorAvailable.crear(keySurtidores, cantSurtidores);
		
		// Me uno al contador de playeros disponibles
		m_CantPlayerosAvailable.unirse(pathContadorPlayeros,ProjectId::CONTADOR_PLAYEROS);
		
		// Abro la caja
		m_Caja.abrir();
	}
	catch(const BaseException& e)
	{
		std::stringstream eMsg;
		eMsg << "PLAYERO >> El jefe no se inicio correctamente." << e.what();
		m_Log->info(eMsg.str());
		throw BaseException(eMsg.str(), e.getCode());
	}
}

Playero::~Playero()
{
	
}

void Playero::joinChiefClientQueue()
{
	// Intento unirme al fifo del jefe para registrarme
	std::string senderFifoPathName = ConfigurationFile::getProperty(Properties::COLA_PLAYERO);
	bool protectAccess = true;
	m_ReceptorJefe.crear(senderFifoPathName, CreationMode::DEBE_EXISTIR, protectAccess);
	
	// Primera iteracion: Incremento el contador de playeros libres para asegurarme que el jefe abra el fifo
	this->incrementPlayerosAvailable();
	m_CantPlayerosAvailableIncremented = true;
	
	m_ReceptorJefe.abrir();
}

void Playero::createInstructionReceptor()
{
	// Creo un fifo de recepcion donde me notificaran instrucciones
	std::string strPID = Utils::convertIntToStr(getpid());
	m_ReceptorFifoPathName = "/tmp/playero_" + strPID;
	
	m_Log->debug("Creando receptor "+m_ReceptorFifoPathName);
	bool protectAccess = false;
	m_ReceptorFifo.crear(m_ReceptorFifoPathName,CreationMode::NO_DEBE_EXISTIR, protectAccess);
}

void Playero::checkInAttQueue()
{
	try
	{
		// Si aun no lo hice, incremento el contador de playeros libres
		if(!m_CantPlayerosAvailableIncremented)
			this->incrementPlayerosAvailable();
		
		// Agrego el nombre de mi receptor al fifo del jefe
		m_MessageTxRx.enviarAtributoAReceptor(m_ReceptorJefe,MessageKey::PATH_FIFO,m_ReceptorFifoPathName);
	}
	catch (const BaseException& e)
	{
		std::stringstream eMsg;
		eMsg << "No se pudo registrar en cola de atencion a clientes del jefe.";
		eMsg << e.what() << ". Cliente se retira";
		throw BaseException(eMsg.str(), Playero::RETREATE_EXIT);
	}
}

std::string Playero::retrieveInstructions()
{
	this->abrirReceptorFifo();
	return m_MessageTxRx.obtenerAtributoDeReceptor(m_ReceptorFifo, MessageKey::INSTRUCCION);
}


bool Playero::shouldLeave(const std::string& instruction) const
{
	return (instruction.compare(InstructionValue::FINALIZAR) == 0);
}

bool Playero::hasToAssistClient(const std::string& instruction) const
{
	return (instruction.compare(InstructionValue::ATENDER) == 0);
}

void Playero::assistClient()
{
	m_Log->debug("PLAYERO >> Obteniendo datos del cliente a atender");
	std::string clientFifoPath = m_MessageTxRx.obtenerAtributoDeReceptor(m_ReceptorFifo, MessageKey::PATH_FIFO);
	
	m_Log->debug("PLAYERO >> Esperando surtidor libre");
	m_SurtidorAvailable.p();
	
	m_Log->debug("PLAYERO >> Pidiendo monto requerido al cliente");
	bool protectAccess = true;
	FifoEscritura receptorCliente(clientFifoPath, CreationMode::DEBE_EXISTIR, protectAccess);
	receptorCliente.abrir();
	receptorCliente.lock();
	bool shouldLock = false;
	m_MessageTxRx.enviarAtributoAReceptor(receptorCliente, MessageKey::INSTRUCCION, InstructionValue::DAME_MONTO, shouldLock);
	m_MessageTxRx.enviarAtributoAReceptor(receptorCliente, MessageKey::PATH_FIFO, m_ReceptorFifoPathName, shouldLock);
	receptorCliente.unlock();
	receptorCliente.cerrar();
	
	m_Log->debug("PLAYERO >> Esperando respuesta del cliente con el monto deseado");
	std::string requiredAmount = m_MessageTxRx.obtenerAtributoDeReceptor(m_ReceptorFifo, MessageKey::TOMA_MONTO);
	
	m_Log->debug("PLAYERO >> Liberando surtidor");
	m_SurtidorAvailable.v();
	
	// Guardo el cobro
	this->guardarCobro(requiredAmount);
	
	// Reseteo flag de contador de playeros libres
	m_CantPlayerosAvailableIncremented = false;
}

void Playero::guardarCobro(const std::string& monto)
{
	m_Log->debug("PLAYERO >> Guardando cobro: "+monto);
	unsigned int montoInt = Utils::convertSrtToInt(monto);
	m_Caja.guardarRecaudacion(montoInt);
}

void Playero::liberarRecursosCompartidos()
{
	m_SurtidorAvailable.eliminar();
}

void Playero::incrementPlayerosAvailable()
{
	m_Log->debug("PLAYERO >> Incrementando contador de playeros libres");
	
	m_CantPlayerosAvailable.lock();
	int aux = m_CantPlayerosAvailable.leer();
	aux++;
	m_CantPlayerosAvailable.escribir(aux);
	m_CantPlayerosAvailable.unlock();
}

void Playero::abrirReceptorFifo()
{
	if(!m_ReceptorFifoAbierto)
	{
		m_Log->debug("PLAYERO >> Abriendo FIFO receptor de instrucciones");
		m_ReceptorFifo.abrir();
		m_ReceptorFifoAbierto = true;
	}
}
